#!/usr/bin/python3
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (C) 2021 Intel Corporation
#                    Paweł Marczewski <pawel@invisiblethingslab.com>

import os
import subprocess
import sys

import click

from graminelibos import util_tests, _CONFIG_SGX_ENABLED

def change_dir(_ctx, _param, value):
    if value:
        os.chdir(value)

@click.group(help='Helper program for running Gramine tests.')
@click.option('--sgx/--no-sgx', default=(os.environ.get('SGX') == '1'),
              help='Enable SGX mode (you can also set SGX=1 before running)')
@click.option('--conf-file-name', '-n', type=click.Path(exists=True, dir_okay=False),
              default='tests.toml', help='Configuration file name to use')
@click.option('--directory', '-C', callback=change_dir, expose_value=False, is_eager=True,
              metavar='dir', type=click.Path(file_okay=False),
              help='Change directory before running')
@click.pass_context
def main(ctx, sgx, conf_file_name):
    if sgx and not _CONFIG_SGX_ENABLED:
        raise click.ClickException('This version of Gramine is built without SGX')
    ctx.obj = {
        'sgx': sgx,
        'conf_file_name': conf_file_name,
    }


@main.command(help='Rebuild the build.ninja file. Used internally by Ninja.')
@click.pass_context
def regenerate(ctx):
    util_tests.gen_build_file(ctx.obj['conf_file_name'])


@main.command(help='Rebuild manifests. This rebuilds either all manifests, or the specified ones.')
@click.pass_context
@click.option('--force/--no-force', '-f', help='Force rebuild')
@click.option('--verbose/--quiet', '-v/-q', help='Show all command lines while building')
@click.argument('names', type=str, nargs=-1)
def build(ctx, force, verbose, names):
    sgx = ctx.obj['sgx']
    names = [strip_suffix(name) for name in names]

    rebuild(sgx, ctx.obj['conf_file_name'], *names, force=force, verbose=verbose)


@main.command(help='Remove all generated files.')
@click.pass_context
def clean(ctx):
    util_tests.gen_build_file(ctx.obj['conf_file_name'])
    try:
        util_tests.run_ninja(['-t', 'clean', '-g'])
    except subprocess.CalledProcessError as e:
        sys.exit(e.returncode)
    for name in ['.ninja_deps', '.ninja_log']:
        if os.path.exists(name):
            print(f'deleting {name}')
            os.unlink(name)


@main.command(
    help='Run Gramine on a test program, rebuilding the manifest first. '
    'Arguments will be passed to the test program.',
    context_settings={'ignore_unknown_options': True},
)
@click.option('--force/--no-force', '-f', help='Force rebuild')
@click.option('--verbose/--quiet', '-v/-q',
              help='Show all command lines while building')
@click.argument('name', type=str)
@click.argument('args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def run(ctx, force, verbose, name, args):
    sgx = ctx.obj['sgx']
    name = strip_suffix(name)

    rebuild(sgx, ctx.obj['conf_file_name'], name, force=force, verbose=verbose)
    util_tests.exec_gramine(sgx, name, args)


# NOTE: We do not accept the `-v/-q` short options in the below command, because Pytest accepts the
# same options. We could instruct the user to run `gramine-test pytest -- -v` as a workaround, but
# that would make `gramine-test` harder to use.
@main.command(
    help='Run Pytest, rebuilding all manifests first. Arguments will be passed to Pytest.',
    context_settings={'ignore_unknown_options': True},
)
@click.option('--force/--no-force', '-f', help='Force rebuild')
@click.option('--verbose/--quiet', help='Show all command lines while building')
@click.argument('args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def pytest(ctx, force, verbose, args):
    sgx = ctx.obj['sgx']

    rebuild(sgx, ctx.obj['conf_file_name'], force=force, verbose=verbose)
    util_tests.exec_pytest(sgx, args)


def strip_suffix(name):
    '''
    Retrieve manifest name without suffix. Allows the user to pass *.manifest or *.manifest.template
    files (e.g. using tab-completion).
    '''

    for suffix in ('.manifest.template', '.manifest'):
        if name.endswith(suffix):
            return name[:-len(suffix)]
    return name


def rebuild(sgx, conf_file_name, *names, force=False, verbose=False):
    util_tests.gen_build_file(conf_file_name)
    verbosity = ['-v'] if verbose else []
    host = 'sgx' if sgx else 'direct'
    if names:
        targets = [f'{host}-{name}' for name in names]
    else:
        targets = [host]
    try:
        if force:
            util_tests.run_ninja(verbosity + ['-t', 'clean'] + targets)
        util_tests.run_ninja(verbosity + targets)
    except subprocess.CalledProcessError as e:
        sys.exit(e.returncode)


if __name__ == '__main__':
    main() # pylint: disable=no-value-for-parameter
